package com.beordie.version_1;

import com.beordie.annotation.*;
import com.beordie.utils.StringUtil;
import com.sun.source.tree.NewArrayTree;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @Description
 * @Date 2021/12/28 14:14
 * @Created 30500
 */
public class DispatchServlet extends HttpServlet {
    /**
     * 配置对象
     */
    private Properties properties = new Properties();

    /**
     * 路径下所有类全名
     */
    private List<String> classes = new ArrayList<>();

    /**
     * 实例对象数组
     */
    private Map<String, Object> instances = new HashMap();

    /**
     * url 和 方法的对应关系
     */
    private Map<String, Method> handlerMapping = new HashMap<>();
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            resp.getWriter().write("500 " + Arrays.toString(e.getStackTrace()));
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
        String uri = req.getRequestURI();
        String path = req.getContextPath();
        String url = uri.replace(path, "").replaceAll("/+", "/");
        if (!this.handlerMapping.containsKey(uri)) {
            resp.getWriter().write("404 url was not defined");
            return;
        }

        Method method = this.handlerMapping.get(url);
        String name = StringUtil.lowerCaseFisrt(method.getDeclaringClass().getSimpleName());

        //  填充参数
        Map<String, Integer> paramIndex = new HashMap<>();
        Annotation[][] annotations = method.getParameterAnnotations();
        for (int i = 0; i < annotations.length; i++) {
            for (Annotation annotation : annotations[i]) {
                if (!(annotation instanceof BODRequestParam)) { continue; }
                String paramName = ((BODRequestParam) annotation).name();
                if (!"".equals(paramName.trim())) {
                    paramIndex.put(paramName, i);
                }
            }
        }
        //  获取参数位置
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> type = parameterTypes[i];
            if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                paramIndex.put(type.getName(), i);
            }
        }
        //  设置参数详情
        Object[] params = new Object[parameterTypes.length];
        Map<String, String[]> parameterMap = req.getParameterMap();
        for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
            if (!paramIndex.containsKey(param.getKey())) { continue; }
            String string = Arrays.toString(param.getValue())
                    .replaceAll("\\[|\\]", "")
                    .replaceAll("\\s", "");
            params[paramIndex.get(param.getKey())] = string;
        }
        Object invoke = method.invoke(this.instances.get(name), params);
        resp.setCharacterEncoding("utf8");
        resp.getWriter().write(invoke.toString());
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //  1.加载配置文件信息流
        String parameter = config.getInitParameter("config-name");
        InputStream stream = DispatchServlet.class.getClassLoader().getResourceAsStream(parameter);
        try {
            this.properties.load(stream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //  2.扫描相关类
        scanPackage(this.properties.getProperty("base"));

        //  3.初始化 IOC 容器对象
        try {
            doInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Iterator<Map.Entry<String, Object>> iterator = this.instances.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> next = iterator.next();
            System.out.println(next.getKey() + "  " + next.getValue());
        }

        //  4、DI 注入
        doInjection();

        //  5、建立 url 映射
        doHandlerMapping();

        System.out.println("初始化数据完成");
    }

    private void doHandlerMapping() {
        if (this.instances.size() == 0) { return ; }
        for (Map.Entry<String, Object> entry : this.instances.entrySet()) {
            Class<?> aClass = entry.getValue().getClass();
            if (!aClass.isAnnotationPresent(BODController.class)) { continue; }
            String baseUrl = null;
            if (aClass.isAnnotationPresent(BODRequestMapping.class)) {
                baseUrl = aClass.getAnnotation(BODRequestMapping.class).value();
            }
            for (Method method : aClass.getMethods()) {
                if (!method.isAnnotationPresent(BODRequestMapping.class)) { continue; }
                BODRequestMapping annotation = method.getAnnotation(BODRequestMapping.class);
                String url = ("/" + baseUrl + "/" + annotation.value()).replaceAll("/+", "/");
                this.handlerMapping.put(url, method);
                System.out.println("mapped : " + url + " ---> " + method);
            }
        }
    }

    private void doInjection() {
        if (this.instances.size() == 0) {return ;}
        for (Map.Entry<String, Object> entry : this.instances.entrySet()) {
            for (Field field : entry.getValue().getClass().getDeclaredFields()) {
                String name = null;
                if (field.isAnnotationPresent(BODAutowried.class)) { // 按照类型注入
                    name = field.getName();
                } else if (field.isAnnotationPresent(BODResource.class)) { // 按照名称进行注入
                    name = field.getAnnotation(BODResource.class).name().trim();
                    if ("".equals(name)) name = field.getName();
                } else {
                    continue;
                }

                //  字段进行赋值
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(), this.instances.get(name));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private void doInstance() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        if (this.classes.size() == 0) return;
        for (String clazz : this.classes) {
            Class<?> aClass = DispatchServlet.class.getClassLoader().loadClass(clazz);
            // 服务层对象
            if (aClass.isAnnotationPresent(BODService.class)) {
                BODService annotation = aClass.getAnnotation(BODService.class);
                String name = annotation.name();
                Object instance = aClass.getDeclaredConstructor().newInstance();
                if (name != null && !"".equals(name)) {
                    instances.put(name, instance);
                } else {
                    String simpleName = aClass.getSimpleName();
                    instances.put(new String(StringUtil.lowerCaseFisrt(simpleName)), instance);
                }
                // 有接口实现
                Class<?>[] interfaces = aClass.getInterfaces();
                for (Class<?> inter : interfaces) {
                    String simpleName = inter.getName();
                    if (instances.containsKey(simpleName)) {
                        throw new RuntimeException("多个接口实例初始化");
                    }
                    char[] chars = simpleName.toCharArray();
                    instances.put(StringUtil.lowerCaseFisrt(simpleName), instance);
                }
            } else if (aClass.isAnnotationPresent(BODController.class)) {
                BODController annotation = aClass.getAnnotation(BODController.class);
                String simpleName = aClass.getSimpleName();
                Object instance = aClass.getDeclaredConstructor().newInstance();
                instances.put(StringUtil.lowerCaseFisrt(simpleName), instance);
            }
        }
    }

    private void scanPackage(String packageName) {
        URL url = DispatchServlet.class.getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        File file = new File(url.getFile());
        for (File path : file.listFiles()) {
            // 包继续遍历
            if (path.isDirectory()) {
                scanPackage(packageName + "." + path.getName());
            } else {
                // 非字节文件不进行处理
                if (!path.getName().endsWith("class")) {
                    continue;
                }
                String className = packageName + "." + path.getName().substring(0, path.getName().lastIndexOf(".class"));
                this.classes.add(className);
            }
        }
    }
}
